package com.weg.ide.common;

import org.eclipse.core.commands.ExecutionException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.text.undo.DocumentUndoEvent;
import org.eclipse.text.undo.DocumentUndoManagerRegistry;
import org.eclipse.text.undo.IDocumentUndoListener;
import org.eclipse.text.undo.IDocumentUndoManager;

/**
 * @project: JvIDE
 * @author: WEG
 * @description:
 **/
public class UndoManager {


    /**
     * The undo level
     */
    private int fUndoLevel;
    /**
     * The document undo manager that is active.
     */
    private IDocumentUndoManager fDocumentUndoManager;
    /**
     * The document that is active.
     */
    private IDocument fDocument;
    /**
     * The document undo listener
     */
    private IDocumentUndoListener fDocumentUndoListener;

    public UndoManager() {
        this(Integer.MAX_VALUE);
    }

    /**
     * Creates a new undo manager who remembers the specified number of edit commands.
     *
     * @param undoLevel the length of this manager's history
     */
    public UndoManager(int undoLevel) {
        fUndoLevel = undoLevel;
    }

    public void commit() {
        if (isConnected())
            fDocumentUndoManager.commit();
    }

    /**
     * Returns whether this undo manager is connected to a text viewer.
     *
     * @return <code>true</code> if connected, <code>false</code> otherwise
     */
    private boolean isConnected() {
        return fDocument != null && fDocumentUndoManager != null;
    }

    /*
     * @see IUndoManager#beginCompoundChange
     */
    public void beginCompoundChange() {
        if (isConnected()) {
            fDocumentUndoManager.beginCompoundChange();
        }
    }

    /*
     * @see IUndoManager#endCompoundChange
     */
    public void endCompoundChange() {
        if (isConnected()) {
            fDocumentUndoManager.endCompoundChange();
        }
    }

    public void setMaximalUndoLevel(int undoLevel) {
        fUndoLevel = Math.max(0, undoLevel);
        if (isConnected()) {
            fDocumentUndoManager.setMaximalUndoLevel(fUndoLevel);
        }
    }

    public void connect(IDocument document) {
        if (document == null) return;
        connectDocumentUndoManager(document);
    }

    public void disconnect() {
        disconnectDocumentUndoManager();
    }

    public void reset() {
        if (isConnected())
            fDocumentUndoManager.reset();
    }

    public boolean canRedo() {
        if (isConnected())
            return fDocumentUndoManager.redoable();
        return false;
    }

    public boolean canUndo() {
        if (isConnected())
            return fDocumentUndoManager.undoable();
        return false;
    }

    public void redo() {
        if (!isConnected()) return;
        try {
            fDocumentUndoManager.redo();
        } catch (ExecutionException ex) {
            ex.printStackTrace();
        }
    }

    public void undo() {
        if (!isConnected()) return;
        try {
            fDocumentUndoManager.undo();
        } catch (ExecutionException ex) {
            ex.printStackTrace();
        }
    }

    private void connectDocumentUndoManager(IDocument document) {
        disconnectDocumentUndoManager();
        if (document != null) {
            fDocument = document;
            DocumentUndoManagerRegistry.connect(fDocument);
            fDocumentUndoManager = DocumentUndoManagerRegistry.getDocumentUndoManager(fDocument);
            fDocumentUndoManager.connect(this);
            setMaximalUndoLevel(fUndoLevel);
            fDocumentUndoListener = new DocumentUndoListener();
            fDocumentUndoManager.addDocumentUndoListener(fDocumentUndoListener);
            reset();
        }
    }

/*    private IUndoContext getUndoContext() {
        if (isConnected())
            return fDocumentUndoManager.getUndoContext();
        return null;
    }*/

    private void disconnectDocumentUndoManager() {
        if (fDocumentUndoManager != null) {
            fDocumentUndoManager.disconnect(this);
            DocumentUndoManagerRegistry.disconnect(fDocument);
            fDocumentUndoManager.removeDocumentUndoListener(fDocumentUndoListener);
            fDocumentUndoListener = null;
            fDocumentUndoManager = null;
        }
        if (fDocument != null)
            fDocument = null;
    }

    /**
     * Internal document undo listener.
     */
    private static class DocumentUndoListener implements IDocumentUndoListener {

        @Override
        public void documentUndoNotification(DocumentUndoEvent event) {

        }
    }
}

